#!/usr/bin/env python2

import os
import sys
import socket
import time
import subprocess
import fcntl
import types
import errno
import getopt
import random

from config import Config
from storage import Storage
from utils import Exp, _dmsg, _dwarn, _derror, _str2dict, _getrack, _exec_pipe
from moveobject import Movebycapcity, Movebylocation, Movebyconnection
from movemetadata import MoveMetadata

class ClusterScan:
    def __init__(self, config, home = None):
        self.config = config
        self.storage = Storage(self.config, home)
        self.lich_admin = os.path.join(self.config.lich, "libexec/lich.admin")

    def __writeable(self, name):
        try:
            res = _exec_pipe([self.lich_admin, '--stat', name], 3, False)
            res = _str2dict(res)
        except Exp, e:
            res = {}

        a = res.get('writeable')
        #print a, type(a), 'fuck', res, name, type(res)
        if a == 'True':
            return True
        return False

    def _build_tree(self, treelist, objectlist, src):
        a = self.storage.list(src)
        if (len(a) == 0):
            return (treelist, objectlist)

        for i in a:
            cid = self.storage.query(src, i)
            if (self.storage.isdir(cid)):
                #print ("dir:%s" % (cid))
                treelist.append(cid)
                self._build_tree(treelist, objectlist, cid)
            else:
                #print ("notdir:%s" % (cid))
                objectlist.append(cid)
        return (treelist, objectlist)

    def _movemeta(self, chunk):
        _dmsg("move metadata by rack")
        movemeta = MoveMetadata(self.storage, self.config)

        newlen = len(self.config.hosts)
        retry = 0
        while (retry < 100):
            lst1 = self.storage.list_node(True)
            if (len(lst1) < newlen):
                time.sleep(1)
                retry = retry + 1
            else:
                break

        lst2 = {}
        for (k, v) in lst1.items():
            if self.__writeable(k):
                lst2.update({k: v})

        for i in chunk:
            try:
                movemeta.movebylocation(i, lst2)
            except Exp, e:
                if (e.errno == errno.ENOENT or e.errno == errno.EAGAIN):
                    _derror("move %s : %s" % (i, e.err))
                    continue
                else:
                    raise

    def _movedata(self, oarray):
        _dmsg("move rawdata by connection")
        movebyconnection = Movebyconnection(self.storage)
        for i in oarray:
            try:
                movebyconnection.move(i)
            except Exp, e:
                if (e.errno == errno.ENOENT or e.errno == errno.EAGAIN):
                    _derror("move %s : %s" % (i, e.err))
                    continue
                else:
                    raise

        _dmsg("move rawdata by rack")
        self._movemeta(oarray)

    def scan(self):
        _dmsg("scan cluster by admin")
        #self.storage.scan("/")

        garray = []
        rarray = self.storage.list("group")
        for i in rarray:
            chkid = self.storage.query("group", i)
            garray.append(chkid)

        tarray = []
        oarray = []
        tarray.append(self.storage.query("namespace", "root"))
        tarray, oarray = self._build_tree(tarray, oarray, "/")

        self._movemeta(garray + tarray)
        self._movedata(oarray)

def usage():
    print ("usage:")
    print (sys.argv[0] + " --home <path>")
    print (sys.argv[0])

def main():
    try:
        opts, args = getopt.getopt(
                sys.argv[1:], 
                'h', ['home=', 'help']
                )
    except getopt.GetoptError, err:
        print str(err)
        usage()

    config = Config()
    if (len(sys.argv) == 1):
        clusterscan = ClusterScan(config)
        clusterscan.scan()
        return

    for o, a in opts:
        if o in ('--help'):
            usage()
            exit(0)
        elif o == '--home':
            clusterscan = ClusterScan(config, a)
            clusterscan.scan()
        else:
            assert False, 'oops, unhandled option: %s, -h for help' % o
            exit(1)

if __name__ == '__main__':
    main()
